/*
 * ExplicaGUIBuilder.java
 *
 * $Id: ExplicaGUIBuilder.java,v 1.7 2009-09-28 10:17:29 mario Exp $
 */
package org.ceteca.explica.client.gui;

import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Hashtable;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.ceteca.explica.client.ClientContext;
import org.ceteca.javaglade.Gui;
import org.ceteca.javaglade.JavaGladeException;
import org.ceteca.javaglade.JavaGladeUtil;
import org.ceteca.javaglade.Localizer;
import org.ceteca.javaglade.toolkit.swt.SwtToolkit;
import org.ceteca.javaglade.toolkit.swt.SwtTopLevelWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;

public class ExplicaGUIBuilder implements Localizer {
	private static ExplicaGUIBuilder instance;
	private SwtToolkit swtToolkit;
	private Display display;
	private Hashtable<String, Gui> guis;
	
	public final static ExplicaGUIBuilder getInstance() {
		if (instance == null)
			instance = new ExplicaGUIBuilder();
		
		return instance;
	}
	
	private ExplicaGUIBuilder() {
		this.swtToolkit = new SwtToolkit(ClientContext.getInstance().getResourceDir());
		this.display = swtToolkit.getDisplay();
		this.guis = new Hashtable<String, Gui>();
		JavaGladeUtil.getInstance().initLogger(this, "logs", "/org/ceteca/javaglade/log4j.xml");
		JavaGladeUtil.getInstance().configure(this.swtToolkit, ClientContext.getInstance().getI18n(), ClientContext.getInstance().getResourceDir(), this);
	}
	
	public Gui buildGUI(String guiName, String gladeFilename) throws JavaGladeException {
		Gui gui = JavaGladeUtil.getInstance().build(gladeFilename, guiName, SWT.COLOR_WIDGET_BACKGROUND);
		this.guis.put(guiName, gui);
		return gui;
	}
	
	public Gui buildGUI(String guiName, String gladeFilename, int backgroundColor) throws JavaGladeException {
		Gui gui = JavaGladeUtil.getInstance().build(gladeFilename, guiName, backgroundColor);
		this.guis.put(guiName, gui);
		return gui;
	}
	
	public Gui buildGUI(String guiName, String gladeFilename, Gui parentGui, SwtTopLevelWindow parentWindow) throws JavaGladeException {
		Gui gui = JavaGladeUtil.getInstance().build(gladeFilename, guiName, parentGui, parentWindow, SWT.COLOR_WIDGET_BACKGROUND);
		this.guis.put(guiName, gui);
		return gui;
	}
	
	public Gui buildGUI(String guiName, String gladeFilename, Gui parentGui, String rootnode, Widget parent,
			String parentName, String domain) throws JavaGladeException {
		Gui gui = JavaGladeUtil.getInstance().
			build(gladeFilename, guiName, parentGui, rootnode, parent, parentName, domain, SWT.COLOR_WIDGET_BACKGROUND);
		this.printGUIs(new PrintWriter(System.out));
//		this.guis.put(guiName, gui);
		return gui;
	}
	
	public Gui buildGUI(String guiName, String gladeFilename, Gui parentGui, String rootnode, Widget parent,
			String parentName, String domain, int backgroundColor) throws JavaGladeException {
		Gui gui = JavaGladeUtil.getInstance().build(gladeFilename, guiName, parentGui, rootnode, parent, parentName, domain, backgroundColor);
		this.guis.put(guiName, gui);
		return gui;
	}
	
	public void asyncExec(Method method, Object instance, Object[] params) {
		GUIOperation guiOp = new GUIOperation(method, instance, params);
		if (this.display != null) {
			this.display.asyncExec(guiOp);
		}
	}
	
	public void syncExec(Method method, Object instance, Object[] params) {
		GUIOperation guiOp = new GUIOperation(method, instance, params);
		if (this.display != null) {
			this.display.syncExec(guiOp);
		}
	}
	
	public Gui getGUI(String guiName) {
		return this.guis.get(guiName);
	}
	
	/**
	 * Connect a signal (event) to its correspondent event handler.
	 * @param signalName String, SWT name of the signal (event type).
	 * @param widget Widget, SWT widget in which the events take place.
	 * @param handler String, handler method's name.
	 * @param objHandler Object, instance of the class that implements the handler.
	 */
	public void connectSignal(String signalName, Widget widget, String handler, Object objHandler) {
		this.swtToolkit.connectSignal(signalName, widget, handler, objHandler);
	}
	
	public SwtToolkit getToolkit() {
		return this.swtToolkit;
	}
	
	public Display getDisplay() {
		return this.display;
	}
	
	public void printGUIs(PrintWriter out) {
		if (this.guis != null && !this.guis.isEmpty()) {
			Iterator it = this.guis.keySet().iterator();
			while (it.hasNext()) {
				Object key = it.next();
				Gui gui = this.guis.get(key);
				System.out.println("Gui got: " + key + "[" + gui + "]");
				if (gui.getParentGui() == null)
					out.println(printGUI(gui, ""));	
			}
		}
		else
			out.println("NO GUIS AVAILABLE");
	}
	
	public void printGUIs(Logger logger) {
		StringBuffer buf = new StringBuffer();
		if (this.guis != null && !this.guis.isEmpty()) {
			Iterator it = this.guis.keySet().iterator();
			while (it.hasNext()) {
				Object key = it.next();
				Gui gui = this.guis.get(key);
				System.out.println("Gui got: " + key + "[" + gui + "]");
				if (gui.getParentGui() == null)
					buf.append(printGUI(gui, ""));	
			}
		}
		else
			logger.info("NO GUIS AVAILABLE");
		
		logger.info(buf.toString());
	}
	
	private String printGUI(Gui gui, String prefix) {
		StringBuffer result = new StringBuffer();
		
		result.append("\n");
		result.append(prefix);
		result.append(gui.getGuiName());
		result.append(" = ");
		result.append(gui);
		System.out.println("Gui " + gui.getGuiName() + " Has children? " + (gui.getChildren() != null && !gui.getChildren().isEmpty()));
		if (gui.getChildren() != null && !gui.getChildren().isEmpty()) {
			for (Gui childGui : gui.getChildren()) {
				result.append(printGUI(childGui, prefix + "\t"));
			}
		}
		
		return result.toString();
	}
	
	/**
	 * Shows a dialog with a confirmation message. It suspends the execution of the 
	 * operation until the user selects an option in the confirmation window. Finally
	 * it returns the value selected by the user.
	 * @param parentWindow Shell, parent window of the confirmation dialog.
	 * @param message String, message to the user.
	 * @param title String, title of the confirmation dialog window.
	 * @param dialogType int, type of the dialog icon. It may be one of the next: 
	 * ICON_ERROR, ICON_INFORMATION, ICON_QUESTION, ICON_WARNING, ICON_WORKING
	 * @param buttons int, buttons showed in the dialog. Each button represents
	 * one of the options the user may choose. It may be one or more of the following:
	 * YES | NO | CANCEL, OK | CANCEL,  ABORT | RETRY | IGNORE | CANCEL
	 * @return int, option chosen by the user. It's the pressed button ID.
	 */
	public int showConfirmationDialog(Shell parentWindow, String message, String title, int dialogType, int buttons) {
		if (parentWindow == null) {
			parentWindow = new Shell(ExplicaGUIBuilder.getInstance().getDisplay());
			parentWindow.setVisible(false);
		}
		MessageBox messageBox = new MessageBox(parentWindow, dialogType | buttons);
        
        messageBox.setText(title);
        messageBox.setMessage(message);
        int buttonID = messageBox.open();

        return buttonID;
	}
	
	public Color getColor(int color) {
		return this.display.getSystemColor(color);
	}

	public InputStream getInputStream(String path) {
		return this.getClass().getResourceAsStream(path);
	}

	public URL getCompletePath(String path) {
		return this.getClass().getResource(path);
	}
	
	public Gui getGui(String guiName) {
		if (this.guis != null)
			return this.guis.get(guiName);
		else
			return null;
	}
	
	/**
	 * Returns tag Id assigned to CVS source file.
	 */
	public static String getRevision() {
		return "$Id: ExplicaGUIBuilder.java,v 1.7 2009-09-28 10:17:29 mario Exp $";
	}
}
